package main

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"text/template"
)

var (
	in    = flag.String("in", "/dev/stdin", "The file to read from")
	out   = flag.String("out", "/dev/stdout", "The file to write to")
	check = flag.Bool("check", true, "Validate the question")
)

type question struct {
	Title    string `json:"title"`
	Question string `json:"question"`
	Feedback string `json:"feedback"`
	Options  []struct {
		Answer   string `json:"answer"`
		Points   int    `json:"points"`
		Feedback string `json:"feedback"`
	} `json:"options"`
}

func checkQuestions(q []question) {
	if len(q) != 2 {
		log.Printf("WARNING: you need 2 questions, you have %d.", len(q))
	}

	for i, v := range q {
		if len(v.Options) != 4 {
			log.Printf("WARNING: you need 4 options per question, you have %d for question %d.", len(v.Options), i+1)
		}

		perfectScore := 0
		halfScore := 0
		noScore := 0
		for j, v := range v.Options {
			switch {
			case v.Points > 100:
				log.Printf(
					"WARNING: option %d for question %d is greater than 100",
					j+1,
					i+1,
				)
			case v.Points == 100:
				perfectScore++
			case v.Points == 0:
				noScore++
			case v.Points > 1 && v.Points <= 50:
				halfScore++
			default:
				log.Printf(
					"WARNING: option %d for question %d is between 51 and 100 (this is not allowed)",
					j+1,
					i+1,
				)
			}
		}

		if perfectScore > 1 {
			log.Printf("WARNING: question %d has more than 1 correct answer", i+1)
		}
		if noScore < 2 {
			log.Printf("WARNING: question %d has fewer than 2 incorrect answers", i+1)
		}
		if halfScore > 1 {
			log.Printf("WARNING: question %d has more than 1 partially correct answer", i+1)
		}
	}
}

func main() {
	flag.Parse()

	var (
		outw io.Writer
		inw  io.Reader
	)

	if *in == "/dev/stdin" {
		inw = os.Stdin
	} else {
		infile, err := os.Open(*in)
		if err != nil {
			log.Fatalf("could not open out file: %v", err)
			return
		}

		inw = infile
	}

	if *out == "/dev/stdout" {
		outw = os.Stdout
	} else {
		ofile, err := os.Create(*out)
		if err != nil {
			log.Fatalf("could not open out file: %v", err)
			return
		}

		outw = ofile
	}

	var questions []question
	dec := json.NewDecoder(inw)
	err := dec.Decode(&questions)
	if err != nil {
		log.Fatalf("could not decode JSON: %v", err)
		return
	}

	if *check {
		checkQuestions(questions)
	}

	tw := tar.NewWriter(outw)
	var buf bytes.Buffer
	for i := 0; i < len(questions); i++ {
		buf.Reset()
		err = tmpl.Execute(&buf, questions[i])
		if err != nil {
			log.Fatalf("could not execute template: %v", err)
			return
		}

		hdr := &tar.Header{
			Name: fmt.Sprintf("%d.csv", i),
			Mode: int64(os.ModePerm),
			Size: int64(buf.Len()),
		}
		err = tw.WriteHeader(hdr)
		if err != nil {
			log.Fatalf("could not write tar header: %v", err)
			return
		}

		_, err = buf.WriteTo(tw)
		if err != nil {
			log.Fatalf("could not write tar body: %v", err)
			return
		}
	}

	err = tw.Close()
	if err != nil {
		log.Fatalf("could not finalize tar file: %v", err)
		return
	}
}

var tmpl *template.Template

func init() {
	const tmplText = `NewQuestion,MC,
Title,{{quote .Title}},
QuestionText,{{quote .Question}},{{range .Options}}
Option,{{.Points}},{{quote .Answer}},,{{quote .Feedback}}{{end}}
Feedback,{{quote .Feedback}},,,,
`
	funcMap := template.FuncMap{
		"quote": quote,
	}
	tmpl = template.Must(template.New("q").Funcs(funcMap).Parse(tmplText))
}

func quote(s string) string {
	return `"` + strings.Replace(s, `"`, `\\"`, -1) + `"`
}
